package org.dei.perla.sys.device.fpc;

import java.util.ArrayList;
import org.dei.perla.component.LoggableComponent;
import org.dei.perla.component.Startable;
import org.dei.perla.parser.clauses.OnUnsupportedSampleRateOptions;
import org.dei.perla.sys.llqruntime.datastructures.Record;
import org.dei.perla.utils.logger.LogRecord.Type;
import org.dei.perla.utils.logger.LogRecord.Verbosity;
import org.dei.perla.utils.messages.AdapterFpcMessage;
import org.dei.perla.utils.pipe.Pipe;
import org.dei.perla.utils.waiter.Waitable;
import org.dei.perla.utils.waiter.Waiter;

public abstract class FunctionalityProxyComponent extends LoggableComponent implements Startable {
	
	private final Pipe<AdapterFpcMessage> inputPipe;
	private final Waiter<Pipe<AdapterFpcMessage>> inputPipeWaiter;
	private final ArrayList<Pipe<Record>> outputPipeArrayList;
	private class PipeReader implements Runnable {
		
		public void run() {
			Waitable<Pipe<AdapterFpcMessage>> waitable;
			Pipe<AdapterFpcMessage> pipe;
			AdapterFpcMessage message;
			
			while(true) {
				waitable = inputPipeWaiter.waitNext();
				pipe = waitable.getParentComponent();
				message = pipe.dequeue();
				
				if (message != null) {
				 	readMessageFromAdapter(message.getPayload());
				} else {
					inputPipeWaiter.removeWaitable(waitable);
					putNullOnOutputPipeArrayList();
					pipe.stop();
					pipeClosedCallBack(pipe);
					break;
				}
			}
			loggerNotify(Type.Information, getName() + " terminated!", Verbosity.Low);
		}

	}
	
	/**
	 * Indica se il componente è attualmente avviato
	 */
	protected boolean started;
	
	protected abstract void initDefault();
	
	protected abstract void pipeClosedCallBack(Pipe<AdapterFpcMessage> pipe);
	
	synchronized final protected void putNullOnOutputPipeArrayList(){
		for(Pipe<?> p: outputPipeArrayList)
			p.enqueue(null);
	}

	protected abstract void readMessageFromAdapter(byte[] payload);
	
	synchronized final protected Pipe<Record> getNewOutputPipe(){
		Pipe<Record> pipe = new Pipe<Record>(this.getName() + "_PIPE_" + (outputPipeArrayList.size()+1));
		pipe.start();
		outputPipeArrayList.add(pipe);
		return pipe;
	}
	
	public FunctionalityProxyComponent(String parName, Pipe<AdapterFpcMessage> inputPipe) {
		super(parName);		
		this.started = false;
		this.inputPipe = inputPipe;
		this.inputPipeWaiter = new Waiter<Pipe<AdapterFpcMessage>>();
		this.inputPipeWaiter.addWaitable(inputPipe.getWaitable());
		this.outputPipeArrayList = new ArrayList<Pipe<Record>>();
		this.initDefault();
	}
	
	@Override
	synchronized final public boolean isStarted() {
		return started;
	}

	@Override
	synchronized public void start() {
		inputPipeWaiter.start();
		new Thread(new PipeReader()).start();
		started = true;
	}

	@Override
	synchronized public void stop() {
		inputPipe.enqueue(null);
		started = false;
	}

	/**
	 * Setta il tempo di sampling
	 */
	public abstract boolean setSamplingTime(long parTime, OnUnsupportedSampleRateOptions parBehaviour);
	
	/**
	 * Registra una nuova query da eseguire sul'FPC
	 * @param parAttrbiutesNames lista contenente i nomi degli attributi richiesti dalla query
	 * @return pipe sulla quale l'FPC restituira' i record, null nel caso si sia verificato un errore
	 */
	public abstract Pipe<Record> registerQuery(ArrayList<String> parAttrbiutesNames);
}
